package com.wbs.rest;



import com.wbs.rest.config.SqlRestAutoBeanConfig;
import com.wbs.rest.executor.SqlExecutor;
import com.wbs.rest.model.*;
import com.wbs.rest.resource.ResourceType;
import com.wbs.rest.resource.RestSqlResource;
import com.wbs.rest.resource.SqlLinkBuilder;
import com.wbs.rest.resource.SqlResourceInformation;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.Links;
import org.springframework.hateoas.ResourceSupport;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

@Transactional
@RestController
@RequestMapping("${rest-sql.baseUri}")
public class RestSqlController {

    private static final String LINK_HEADER = "Link";

    SqlModelResolver sqlModelResolver;

    public RestSqlController(SqlModelResolver sqlModelResolver) {
        this.sqlModelResolver = sqlModelResolver;
    }

    @RequestMapping(value = SqlRestAutoBeanConfig.MAPPING_URI, method = RequestMethod.GET)
    public ResponseEntity<ResourceSupport> getCollectionResource ( SqlResourceInformation resourceInformation) throws Exception {

        resourceInformation.verifySupportedMethod(HttpMethod.GET, ResourceType.COLLECTION);

        SqlExecutor executor = resourceInformation.getExecutor();

        if (null == executor) {
            return new ResponseEntity<>(HttpStatus.NOT_FOUND);
        }

        SqlModel sqlModel = resourceInformation.getSqlModel();

        SqlLinkBuilder sqlLinkBuilder = SqlLinkBuilder.linkTo(resourceInformation.getSqlModel());

        return new ResponseEntity<>( new RestSqlResource(executor.execute(sqlModel, resourceInformation.getSqlModelEnum(), HttpMethod.GET), sqlLinkBuilder.withModel()), HttpStatus.OK);
    }


    @RequestMapping(value = SqlRestAutoBeanConfig.MAPPING_URI + "/{id}", method = RequestMethod.GET)
    public ResponseEntity<ResourceSupport> getSingleResource ( SqlResourceInformation resourceInformation, @PathVariable("id") Serializable id) throws Exception {

        resourceInformation.verifySupportedMethod(HttpMethod.GET, ResourceType.SINGLE);

        SqlExecutor executor = resourceInformation.getExecutor();
        SqlModel sqlModel = resourceInformation.getSqlModel();
        if (null == executor || null == sqlModel) {
            return new ResponseEntity<>(HttpStatus.NOT_FOUND);
        }

        sqlModel.setArgs(new Object[]{id});
        Object result = executor.selectOne(sqlModel);

        SqlLinkBuilder sqlLinkBuilder = SqlLinkBuilder.linkTo(resourceInformation.getSqlModel());

        return new ResponseEntity<>(new RestSqlResource(result, sqlLinkBuilder.withModel()), HttpStatus.OK);
    }

    @RequestMapping(value = SqlRestAutoBeanConfig.MAPPING_URI, method = RequestMethod.PUT)
    public ResponseEntity<ResourceSupport> putResource ( SqlResourceInformation resourceInformation) throws Exception {

        resourceInformation.verifySupportedMethod(HttpMethod.PUT, ResourceType.NO_CONTENT_PUT);

        SqlExecutor executor = resourceInformation.getExecutor();

        if (null == executor) {
            return new ResponseEntity<>(HttpStatus.NOT_FOUND);
        }

        SqlModel sqlModel = resourceInformation.getSqlModel();

        SqlLinkBuilder sqlLinkBuilder = SqlLinkBuilder.linkTo(sqlModel);

        return new ResponseEntity<>( new RestSqlResource( executor.execute(sqlModel, resourceInformation.getSqlModelEnum(), HttpMethod.PUT), sqlLinkBuilder.withModel()), HttpStatus.OK);
    }

    @RequestMapping(value = SqlRestAutoBeanConfig.MAPPING_URI, method = {RequestMethod.POST, RequestMethod.PATCH})
    public ResponseEntity<ResourceSupport> postResource ( SqlResourceInformation resourceInformation) throws Exception {

//        resourceInformation.verifySupportedMethod(HttpMethod.POST, ResourceType.NO_CONTENT_POST);

        SqlExecutor executor = resourceInformation.getExecutor();

        if (null == executor) {
            return new ResponseEntity<>(HttpStatus.NOT_FOUND);
        }

        SqlLinkBuilder sqlLinkBuilder = SqlLinkBuilder.linkTo(resourceInformation.getSqlModel());

        return new ResponseEntity<>(new RestSqlResource(
                executor.execute(resourceInformation.getSqlModel(), resourceInformation.getSqlModelEnum(), HttpMethod.POST),
                sqlLinkBuilder.withModel()), HttpStatus.OK);
    }


    @RequestMapping(value = SqlRestAutoBeanConfig.MAPPING_URI, method = RequestMethod.DELETE)
    public ResponseEntity<ResourceSupport> deleteResource ( SqlResourceInformation resourceInformation) throws Exception {

        resourceInformation.verifySupportedMethod(HttpMethod.DELETE, ResourceType.NO_CONTENT_DELETE);

        SqlExecutor executor = resourceInformation.getExecutor();

        if (null == executor) {
            return new ResponseEntity<>(HttpStatus.NOT_FOUND);
        }

        int rows = executor.delete(resourceInformation.getSqlModel());

        SqlLinkBuilder sqlLinkBuilder = SqlLinkBuilder.linkTo(resourceInformation.getSqlModel());

        return new ResponseEntity<>(new RestSqlResource(rows, sqlLinkBuilder.withModel()), HttpStatus.OK);
    }



    @RequestMapping(value = {"", "/"}, method = RequestMethod.OPTIONS)
    public ResponseEntity<?> optionsForCollectionResource(SqlResourceInformation resourceInformation) {

        HttpHeaders headers = new HttpHeaders();

        headers.setAllow(resourceInformation.getSupportedMethod());

        return new ResponseEntity<Object>(headers, HttpStatus.OK);
    }

    @RequestMapping(value = {"", "/"}, method = RequestMethod.HEAD)
    public ResponseEntity<?> headCollectionResource() {

        List<SqlModel> sqlModels = sqlModelResolver.queryAllModels();
        List<Link> links = new ArrayList<>();

        for(SqlModel sqlModel : sqlModels){
            links.add(SqlLinkBuilder.linkTo(sqlModel).withModel());
        }

        links.add(0, SqlLinkBuilder.getDefaultSelfLink());

        HttpHeaders headers = new HttpHeaders();
        headers.add(LINK_HEADER, new Links(links).toString());

        return new ResponseEntity<Object>(headers, HttpStatus.NO_CONTENT);
    }


}
